﻿using System;
using System.Collections;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;

namespace SDL
{
    // A window which contains a single element floating above the frame.
    [ContentProperty("Element")]
    public class DockWindow : FrameworkElement, IDockContainer
    {
        /////////////////////////////////////////////////////////////////////////////
        // Properties

        public Window Window { get; private set; }
        public IDockElement Element
        {
            get { return _element; }
            set
            {
                if( _element != null )
                {
                    RemoveLogicalChild(_element);
                    RemoveVisualChild(_element.AsFrameworkElement());
                }
                _element = value;
                AddLogicalChild(_element);
                AddVisualChild(_element.AsFrameworkElement());
            }
        }

        /////////////////////////////////////////////////////////////////////////////
        // Public Constructor

        public DockWindow()
        {
            _dragging = false;
        }

        /////////////////////////////////////////////////////////////////////////////
        // Public Methods

        public FrameworkElement AsFrameworkElement()
        {
            return this;
        }

        public void Remove(IDockElement element, IDockElement replacement)
        {
            throw new NotImplementedException();
        }

        public bool StartDrag(IDockElement element)
        {
            // if this is the only element, drag the window
            if( element == Element )
            {
                PerformDrag();
                return true;
            }
            // this was not the only element, wait for the tear off
            return false;
        }

        public void AutoSize(Size size)
        {
            // resize the window
            Window.Width = size.Width;
            Window.Height = size.Height;
        }

        public void TearOff(IDockElement element, Point offset, Size size)
        {
            _frame.Window_TearOff(element, offset, size);
        }

        public bool CheckDrop(IDockElement element, Point point)
        {
            return _element.CheckDrop(element, point);
        }

        private Rect GetRect()
        {
            Rect rect = new Rect();
            rect.X = Window.Left;
            rect.Y = Window.Top;
            rect.Width = Window.Width;
            rect.Height = Window.Height;
            return rect;
        }

        /////////////////////////////////////////////////////////////////////////////
        // Internal Constructor

        internal DockWindow(IDockElement element, Point offset, Size size)
        {
            Element = element;
            _offset = offset;
            _size = size;
            _dragging = true;
        }

        /////////////////////////////////////////////////////////////////////////////
        // Internal Methods

        internal void Show(DockManager frame)
        {
            // save the frame
            _frame = frame;

            // create the window
            Window = new Window();
            Window.WindowStyle = WindowStyle.None;
            Window.ShowInTaskbar = false;
            Window.AllowsTransparency = true;
            Window.Background = Brushes.Transparent;
            Window.Owner = _frame.Window;

            // register for events
            Window.Activated += new EventHandler(Window_Activated);

            // make sure an element has been set
            if( Element == null )
            {
                throw new Exception("Tool windows must contain an element.");
            }
            Window.Content = this;

            if( _dragging )
            {
                // position of the window
                Point pos = _frame.Window.PointToScreen(Mouse.GetPosition(_frame.Window));
                Window.Left = pos.X - _offset.X;
                Window.Top = pos.Y - _offset.Y;
            }

            if( _dragging )
            {
                // set the size of the window to the given size
                if( double.IsNaN(Window.Width) )
                {
                    Window.Width = _size.Width;
                }
                if( double.IsNaN(Window.Height) )
                {
                    Window.Height = _size.Height;
                }
            }
            else
            {
                // set the size of the window to the desired size of the element
                Size size = new Size(double.PositiveInfinity,
                                     double.PositiveInfinity);
                Element.Measure(size);
                size = Element.DesiredSize;
                if( double.IsNaN(Window.Width) )
                {
                    Window.Width = size.Width;
                }
                if( double.IsNaN(Window.Height) )
                {
                    Window.Height = size.Height;
                }
            }

            // show the window
            Window.Show();

            if( _dragging )
            {
                // drag the window
                PerformDrag();
            }
        }

        internal void Close()
        {
            Window.Close();
        }

        /////////////////////////////////////////////////////////////////////////////
        // Private Methods

        private void PerformDrag()
        {
            _offset = Mouse.GetPosition(this);
            Window.LocationChanged += new EventHandler(DockWindow_Drag);
            Window.DragMove();
            Window.LocationChanged -= new EventHandler(DockWindow_Drag);
        }

        /////////////////////////////////////////////////////////////////////////////
        // Event Handlers

        private void Window_Activated(object sender, EventArgs e)
        {
            _frame.Window_BringToFront(this);
        }

        private void DockWindow_Drag(object sender, EventArgs e)
        {
            _frame.Window_CheckDrop(_element, PointToScreen(_offset));
        }

        /////////////////////////////////////////////////////////////////////////////
        // Private Data

        private DockManager _frame;
        private IDockElement _element;
        private Point _offset;
        private Size _size;
        private bool _dragging;
        //private Window _previewWindow;

        /////////////////////////////////////////////////////////////////////////////
        // FrameworkElement

        protected override int VisualChildrenCount
        {
            get { return 1; }
        }

        protected override IEnumerator LogicalChildren
        {
            get
            {
                yield return Element;
            }
        }

        protected override Visual GetVisualChild(int index)
        {
            return Element.AsFrameworkElement();
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            Element.Measure(availableSize);
            return Element.DesiredSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            Element.Arrange(new Rect(finalSize));
            return finalSize;
        }
    }
}
